package com.demister.at.model;

import com.demister.at.util.Point;
import java.util.LinkedList;

/**
 * Block represents a rectangle with type value.
 * Block is limited by Point capacity, so if the point value is out of capacity, expcetion is thrown.
 * @author Rock
 */
public class Block implements Comparable<Block> {

    private static LinkedList<Block> pool = new LinkedList<Block>();
    private static int poolCnt = 0;
    private static int used = 0;

    /**
     * Retuen the block of specifc attributes.
     * @param x the x
     * @param y the y
     * @param width the width
     * @param height the height
     * @param type the type
     * @return the block
     */
    public static synchronized Block getBlock(int x, int y, int width, int height, int type) {
        Block b = pool.poll();
        if (b == null) {
            b = new Block();
            poolCnt++;
        }
        b.topLeft = Point.getPoint(x, y);
        b.bottomRight = Point.getPoint(x + width - 1, y + height - 1);
        b.type = type;
        used++;
        return b;
    }

    /**
     * Release the block to pool for reusing.
     * @param b the releasing block
     */
    public static synchronized void releaseBlock(Block b) {
        if (used == 0) {
            return;
        }
        pool.add(b);
        used--;
    }

    /**
     * Return the pool count.
     * @return the pool count
     */
    public static synchronized int getPoolCount() {
        return poolCnt;
    }

    /**
     * Retuen the used count.
     * @return the used
     */
    public static synchronized int getPoolUsed() {
        return used;
    }
    protected Point topLeft;
    protected Point bottomRight;
    protected int type;

    protected Block() {
    }

    /**
     * Return the top left x value of the block.
     * @return the x
     */
    public int getX() {
        return topLeft.getX();
    }

    /**
     * Return the top left y value of the block.
     * @return the y
     */
    public int getY() {
        return topLeft.getY();
    }

    /**
     * Return the width of the block.
     * @return the width
     */
    public int getWidth() {
        return bottomRight.getX() - topLeft.getX() + 1;
    }

    /**
     * Return the height of the block.
     * @return the height
     */
    public int getHeight() {
        return bottomRight.getY() - topLeft.getY() + 1;
    }

    /**
     * Return the type of the block.
     * @return the type
     */
    public int getType() {
        return this.type;
    }

    /**
     * Return the top left point the of block.
     * @return the top left point
     */
    public Point getTopLeftPoint() {
        return this.topLeft;
    }

    /**
     * Return the bottom right point of the block.
     * @return the bottom right point
     */
    public Point getBottomRightPoint() {
        return this.bottomRight;
    }

    /**
     * Return the area of the block.
     * @return the area
     */
    public int getArea() {
        return this.getHeight() * this.getWidth();
    }

    /**
     * Set new top left x value.
     * @param x the new x
     */
    public void setX(int x) {
        int dx = x - topLeft.getX();
        topLeft = Point.getPoint(x, topLeft.getY());
        bottomRight = Point.getPoint(bottomRight.getX() + dx, bottomRight.getY());
    }

    /**
     * Set new top left y value.
     * @param y the new y
     */
    public void setY(int y) {
        int dy = y - topLeft.getY();
        topLeft = Point.getPoint(topLeft.getX(), y);
        bottomRight = Point.getPoint(bottomRight.getX(), bottomRight.getY() + dy);
    }

    public void moveX(int dx) {
        topLeft = Point.getPoint(topLeft.getX() + dx, topLeft.getY());
        bottomRight = Point.getPoint(bottomRight.getX() + dx, bottomRight.getY());
    }

    public void moveY(int dy) {
        topLeft = Point.getPoint(topLeft.getX(), topLeft.getY() + dy);
        bottomRight = Point.getPoint(bottomRight.getX(), bottomRight.getY() + dy);
    }

    /**
     * Set new width of the block.
     * @param width the new width
     */
    public void setWidth(int width) {
        bottomRight = Point.getPoint(topLeft.getX() + width - 1, bottomRight.getY());
    }

    /**
     * Set new height of the block.
     * @param height the new height
     */
    public void setHeight(int height) {
        bottomRight = Point.getPoint(bottomRight.getX(), topLeft.getY() + height - 1);
    }

    /**
     * Set the type of the block.
     * @param type the new type
     */
    public void setType(int type) {
        this.type = type;
    }

    public int compareTo(Block o) {
        return this.topLeft.compareTo(o.topLeft);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj != null || obj instanceof Block) {
            Block b = (Block) obj;
            return this.topLeft.equals(b.topLeft) && this.bottomRight.equals(b.bottomRight);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 67 * hash + (this.topLeft != null ? this.topLeft.hashCode() : 0);
        return hash;
    }

    @Override
    public String toString() {
        return String.format("[Block at %d %d, width %d, height %d, type %d]", getX(), getY(), getWidth(), getHeight(), getType());
    }
}
